package com.freewater.javabase.demo.reflect.methodhandler;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

// 参见：https://blog.csdn.net/ShuSheng0007/article/details/107066856
public class Main {
    /**
     * 1、创建Lookup
     * 2、创建MethodType
     * 3、基于Lookup与MethodType获得MethodHandle
     * 4、调用MethodHandle
     */
    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        //无参数构造器
        MethodType con1Mt = MethodType.methodType(void.class);
        MethodHandle con1Mh = lookup.findConstructor(HandleTarget.class, con1Mt);
        Object target1 = con1Mh.invoke();
        System.out.println(target1);
        //有参数构造器
        MethodType con2Mt = MethodType.methodType(void.class, String.class);
        MethodHandle con2Mh = lookup.findConstructor(HandleTarget.class, con2Mt);
        Object target2 = con2Mh.invoke("ErGouWang");
        System.out.println(target2);
        //调用非private实例方法
        MethodType getterMt = MethodType.methodType(String.class);
        MethodHandle getterMh = lookup.findVirtual(HandleTarget.class, "getName", getterMt);
        String name = (String) getterMh.invoke(target2);
        System.out.println(name);
        //访问private方法
        Method learnMethod = HandleTarget.class.getDeclaredMethod("learnPrograming", String.class);
        learnMethod.setAccessible(true);
        MethodHandle learnProMh = lookup.unreflect(learnMethod);
        learnProMh.invoke(target1, "Java");
        // 访问静态方法
        MethodType decMt = MethodType.methodType(String.class, String.class);
        MethodHandle decMh = lookup.findStatic(HandleTarget.class, "declaration", decMt);
        String dec = (String) decMh.invoke("庄子");
        System.out.println(dec);
        //访问非private属性
        //1、注意这个不是获取field的javabean Getter方法，与其毫无关系。通过这个Getter 方法句柄我们就可以访问到这个属性了。
        try {
            MethodHandle nameMh = lookup.findGetter(HandleTarget.class, "name", String.class);
            System.out.println((String) nameMh.invoke(con1Mh.invoke()));
        } catch (IllegalAccessException e) {
            System.out.println(e.getMessage());
        }
        //2、需要使用借助反射
        Field nameField = HandleTarget.class.getDeclaredField("name");
        nameField.setAccessible(true);
        MethodHandle nameMh = lookup.unreflectGetter(nameField);
        System.out.println(nameMh.invoke(target1));
        //增强MethodHandle
        MethodType connectNameMt = MethodType.methodType(void.class, String.class );
        MethodHandle connectNameMh = lookup.findVirtual(HandleTarget.class, "connectName", connectNameMt);
        connectNameMh = connectNameMh.bindTo(target2);
        connectNameMh.invoke("love CuiHuaNiu");
        System.out.println(getterMh.invoke(target2));
    }
}
